#pragma once

#include "Socket.hpp"
#include "Logger.hpp"
#include <memory>
#include <iostream>
#include <string>
#include <unistd.h>
#include <signal.h>
#include <functional>

using callback_t = std::function<std::string(std::string &)>;

class TcpServer
{
private:
    void HandlerRequest(std::shared_ptr<Socket> sockfd, InetAddr client)
    {
        //使用短服务
        std::string inbuffer; // 接受缓冲区
        ssize_t n = sockfd->Recv(&inbuffer);
        if (n > 0)
        {
            // 在http 中就认为读到的请求是完整的
            std::string send_str = _cb(inbuffer); // 回调出去处理

            sockfd->Send(send_str);
        }
        else if (n == 0)
        {
            LOG(LogLevel::ERROR) << "client : " << client.ToString() << " 退出， me too";
        }
        else
        {
            LOG(LogLevel::FATAL) << "recv error , client: " << client.ToString();
        }
        sockfd->Close();
    }

public:
    TcpServer(uint16_t port, callback_t cb)
        : _port(port), _cb(cb), _listensockfd(std::make_unique<TcpSocket>())
    {
        // 在构建服务器对象的时候就创建其对应的监听套接字
        _listensockfd->BuildTcpSocketServer(_port);
    }
    ~TcpServer() {}

    void Run()
    {
        signal(SIGCHLD, SIG_IGN);
        // 让服务器跑起来，此处处理为长服务
        while (true)
        {
            InetAddr clientaddr(_port);
            // 获取连接
            std::shared_ptr<Socket> sockfd = _listensockfd->Accept(&clientaddr);
            // 判断获取连接是否成功
            if (sockfd == nullptr)
                continue;

            // 到了此处就是获取连接成功，从sockfd 中读取数据 --> 交给其他函数去处理
            // LOG(LogLevel::DEBUG) << "获取连接成功：" << clientaddr.ToString() << ", sockfd: " << sockfd->Sockfd();

            // 创建子进程，让子进程去执行IO，并且父进程不等待子进程
            if (fork() == 0)
            {
                _listensockfd->Close();
                HandlerRequest(sockfd, clientaddr);

                exit(0); // 子进程执行完io就退出
            }
            // 父进程关闭不适用的文件描述符 --> 避免误操作以及文件描述符的泄漏
            sockfd->Close();
        }
    }

private:
    // 指向套接字的智能指针
    std::unique_ptr<Socket> _listensockfd;
    uint16_t _port;
    callback_t _cb;
};